#include "cat.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv) {
  if (argc < 2) {
    printf("No input\n");
    return 1;
  }
  // read flags
  struct flags flags = {0, 0, 0, 0, 0, 0};  // b,e,n,s,t,v
  int arg_pointer = 1;
  while (argv[arg_pointer][0] == '-' && arg_pointer < argc) {
    if (argv[arg_pointer][1] == '-') {
      if (strcmp("--number-nonblank", argv[arg_pointer]) == 0) {
        flags.number_nonblank = 1;
      } else if (strcmp("--squeeze-blank", argv[arg_pointer]) == 0) {
        flags.squeeze_blank = 1;
      } else if (strcmp("--number", argv[arg_pointer]) == 0) {
        flags.number = 1;
      } else {
        printf("Uncorrect flag(s)\n");
        return 1;
      }
    } else {
      int pointer = 1;
      while (argv[arg_pointer][pointer]) {
        switch (argv[arg_pointer][pointer]) {
          case 'b':
            flags.number_nonblank = 1;
            break;
          case 'e':
            flags.show_end_of_line = 1;
            flags.show_hidden = 1;
            break;
          case 'E':
            flags.show_end_of_line = 1;
            break;
          case 'n':
            flags.number = 1;
            break;
          case 's':
            flags.squeeze_blank = 1;
            break;
          case 't':
            flags.show_tabs = 1;
            flags.show_hidden = 1;
            break;
          case 'T':
            flags.show_tabs = 1;
            break;
          default:
            printf("Uncorrect flag(s)\n");
            return 1;
        }
        pointer += 1;
      }
    }
    arg_pointer += 1;
  }

  // working with files
  int line_counter = 1;
  int was_empty_line = 0;
  while (arg_pointer < argc) {
    FILE* file = fopen(argv[arg_pointer], "r");
    if (file) {
      cat(file, flags, &line_counter, &was_empty_line);
      fclose(file);
    } else {
      printf("No such file or directory\n");
    }
    arg_pointer += 1;
  }

  return 0;
}

/*
Reads file line by line.
If line is empty and squeeze flag is on - skips additional empty strings
Numbers lines (if flags are on)
Prints each of char with settings*/
void cat(FILE* file, struct flags flags, int* line_counter,
         int* was_empty_line) {
  unsigned char* line;
  int is_empty;
  while ((line = read_line(file)) != NULL) {
    is_empty = is_line_empty(line);
    if (is_empty) {
      if (flags.squeeze_blank && *was_empty_line) {
        continue;
      }
      *was_empty_line = 1;
    } else {
      *was_empty_line = 0;
    }

    if ((flags.number && !flags.number_nonblank) ||
        (!is_empty && flags.number_nonblank)) {
      printf("%6d\t", *line_counter);
      *line_counter += 1;
    }

    for (unsigned char* line_ptr = line; *line_ptr; line_ptr++) {
      print_char(*line_ptr, flags);
    }
    free(line);
    line = NULL;
  }
  if (line != NULL) {
    free(line);
  }
}

/*
Shows symbol depending on settings:
1) showing hidden chars (uses extended ASCII in form of unsigned char)
2) showing tabs as ^I
3) showing $ at the end of the line*/
void print_char(unsigned char to_print, struct flags flags) {
  if (to_print == '\n') {
    if (flags.show_end_of_line) {
      printf("$");
    }
    printf("\n");
  } else if (flags.show_tabs && to_print == '\t') {
    printf("^I");
  } else if (to_print == '\t') {
    putchar('\t');
  } else if (to_print == ' ') {
    putchar(' ');
  } else if (flags.show_hidden) {
    if (to_print <= 32) {
      putchar('^');
      putchar(to_print + 64);
    } else if (to_print > 32 && to_print < 127) {
      putchar(to_print);
    } else if (to_print == 127 || to_print >= 255) {
      if (to_print != 127) {
        printf("M-");
      }
      printf("^?");
    } else if (to_print >= 128 && to_print < 160) {
      printf("M-");
      putchar('^');
      putchar(to_print - 64);
    } else if (to_print >= 160 && to_print < 255) {
      printf("M-");
      putchar(to_print - 128);
    }
  } else {
    putchar(to_print);
  }
}

/*
Reads file until meets new line or EOF.
Dynamically allocates memory (mult size by 2 when reaching limit)*/
unsigned char* read_line(FILE* file) {
  int len = 8;
  int pointer = 0;
  unsigned char* result = malloc((len + 2) * sizeof(unsigned char));
  int curchar = getc(file);
  if (curchar == EOF) {
    free(result);
    result = NULL;
  } else {
    while (curchar != '\n' && curchar != EOF) {
      if (pointer + 1 >= len) {
        len += 10;
        unsigned char* tmp = NULL;
        while (tmp == NULL) {
          tmp = realloc(result, (len + 2) * sizeof(char));
        }
        result = tmp;
      }
      result[pointer] = (unsigned char)curchar;
      pointer++;
      curchar = getc(file);
    }
    if (curchar == '\n') {
      result[pointer] = '\n';
      pointer++;
    }
    result[pointer] = '\0';
  }
  return result;
}

int is_line_empty(unsigned char* line) {
  int is_empty = 1;
  int pointer = 0;
  while (line[pointer] && is_empty) {
    if (line[pointer] != ' ' && line[pointer] != '\n') {
      is_empty = 0;
    }
    pointer++;
  }
  return is_empty;
}